package com.joker.aigc.oj.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.joker.aigc.oj.domain.dto.problemsubmit.ProblemSubmitAddDTO;
import com.joker.aigc.oj.domain.dto.problemsubmit.ProblemSubmitQueryRequest;
import com.joker.aigc.oj.domain.entity.Problem;
import com.joker.aigc.oj.domain.entity.ProblemSubmit;
import com.joker.aigc.domain.entity.User;
import com.joker.aigc.domain.enums.AppHttpCodeEnum;
import com.joker.aigc.oj.domain.enums.LanguageEnum;
import com.joker.aigc.oj.domain.enums.ProblemDifficultyEnum;
import com.joker.aigc.oj.domain.enums.ProblemSubmitStatusEnum;
import com.joker.aigc.oj.domain.vo.problemsubmit.ProblemSubmitVO;
import com.joker.aigc.oj.domain.vo.problemsubmit.SubmitSummaryVo;
import com.joker.aigc.exception.BusinessException;
import com.joker.aigc.oj.service.JudgeService;
import com.joker.aigc.oj.mapper.ProblemMapper;
import com.joker.aigc.oj.mapper.ProblemSubmitMapper;
import com.joker.aigc.oj.service.ProblemSubmitService;
import com.joker.aigc.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Antares
 * @description 针对表【question_submit(题目提交)】的数据库操作Service实现
 * @createDate 2023-08-24 10:36:35
 */
@Service
@Slf4j
public class ProblemSubmitServiceImpl extends ServiceImpl<ProblemSubmitMapper, ProblemSubmit> implements ProblemSubmitService {

    @Resource
    private ProblemMapper problemMapper;
    @Resource
    private JudgeService judgeService;
    @Resource
    private UserService userService;

    @Override
    public ProblemSubmit doProblemSubmit(ProblemSubmitAddDTO problemSubmitAddDTO, User currentUser) {
        // 校验编程语言是否合法
        String language = problemSubmitAddDTO.getLanguage();
        LanguageEnum languageEnum = LanguageEnum.getEnumByValue(language);
        if (languageEnum == null) {
            throw new BusinessException(AppHttpCodeEnum.PARAMS_ERROR, "不支持的编程语言");
        }
        long problemId = problemSubmitAddDTO.getProblemId();

        // 判断实体是否存在，根据类别获取实体
        Problem problem = problemMapper.selectById(problemId);
        if (problem == null) {
            throw new BusinessException(AppHttpCodeEnum.NOT_EXIST);
        }
        log.info(problem.toString());

        //判断用户是否有正在等待或判题的题，如果有，提交判题失败
        Long userId = currentUser.getId();
        ProblemSubmit submit = lambdaQuery().eq(ProblemSubmit::getUserId, userId)
                .and(wrapper -> wrapper.eq(ProblemSubmit::getStatus, ProblemSubmitStatusEnum.WAITING).or()
                        .eq(ProblemSubmit::getStatus, ProblemSubmitStatusEnum.RUNNING)).one();
        if (submit != null) {
            throw new BusinessException(AppHttpCodeEnum.SUBMIT_ERROR, "提交过于频繁！");
        }

        //将problem的提交数+1
        problemMapper.update(null, new UpdateWrapper<Problem>()
                .setSql("submit_num = submit_num + 1").eq("id", problem.getId()));

        // 是否已提交题目
        // 每个用户串行提交题目
        ProblemSubmit problemSubmit = new ProblemSubmit();
        problemSubmit.setUserId(userId);
        problemSubmit.setProblemId(problemId);
        problemSubmit.setCode(problemSubmitAddDTO.getCode());
        problemSubmit.setLanguage(language);

        // 设置初始状态
        problemSubmit.setStatus(ProblemSubmitStatusEnum.WAITING.getValue());
        problemSubmit.setJudgeInfo("{}");
        boolean save = this.save(problemSubmit);
        if (!save) {
            throw new BusinessException(AppHttpCodeEnum.INTERNAL_SERVER_ERROR, "数据插入失败");
        }
        // 执行判题服务
        ProblemSubmit submitResult = judgeService.doJudge(problemSubmit);
        return submitResult;
    }

    @Override
    public Page<ProblemSubmitVO> listProblemSubmitVoByPage(ProblemSubmitQueryRequest problemSubmitQueryRequest) {
        long pageNum = problemSubmitQueryRequest.getPageNum();
        long pageSize = problemSubmitQueryRequest.getPageSize();
        Long problemId = problemSubmitQueryRequest.getProblemId();
        String language = problemSubmitQueryRequest.getLanguage();
        Page<ProblemSubmit> page = page(new Page<>(pageNum, pageSize),
                new LambdaQueryWrapper<ProblemSubmit>()
                        .select(ProblemSubmit.class, item -> !item.getColumn().equals("code"))
                        .eq(StringUtils.isNotBlank(language),ProblemSubmit::getLanguage,language)
                        .eq(Objects.nonNull(problemId),ProblemSubmit::getProblemId, problemId));
        return getProblemSumbitVO(page,pageNum,pageSize);
    }

    public Page<ProblemSubmitVO> getProblemSumbitVO (Page<ProblemSubmit> page,Long pageNum,long pageSize){

        Page<ProblemSubmitVO> voPage = new Page<>(pageNum, pageSize, page.getTotal());
        List<ProblemSubmitVO> records = page.getRecords().stream().map(ProblemSubmitVO::objToVo).collect(Collectors.toList());
        if(CollectionUtil.isEmpty(records)){
            return voPage;
        }
        List<User> users=userService.listByIds(records.stream().map(ProblemSubmitVO::getUserId).collect(Collectors.toList()));
        // 将查到的user信息放入ProblemSubmitVo中
        Map<Long,String> userMap = users.stream().collect(Collectors.toMap(User::getId, User::getUsername));
        records.forEach(item -> item.setUserName(userMap.get(item.getUserId())));
        voPage.setRecords(records);
        return voPage;
    }

    @Override
    public Page<ProblemSubmitVO> listProblemSubmitVoByUserId(ProblemSubmitQueryRequest problemSubmitQueryRequest) {
        long pageNum = problemSubmitQueryRequest.getPageNum();
        long pageSize = problemSubmitQueryRequest.getPageSize();
        Long problemId = problemSubmitQueryRequest.getProblemId();
        String language = problemSubmitQueryRequest.getLanguage();
        Long uid = problemSubmitQueryRequest.getUid();
        Page<ProblemSubmit> page = page(new Page<>(pageNum, pageSize),
                new LambdaQueryWrapper<ProblemSubmit>()
                        .select(ProblemSubmit.class, item -> !item.getColumn().equals("code"))
                        .eq(StringUtils.isNotBlank(language),ProblemSubmit::getLanguage,language)
                        .eq(Objects.nonNull(uid),ProblemSubmit::getUserId, uid)
                        .eq(Objects.nonNull(problemId),ProblemSubmit::getProblemId, problemId));

        Page<ProblemSubmitVO> voPage = new Page<>(pageNum, pageSize, page.getTotal());
        List<ProblemSubmitVO> records = page.getRecords().stream().map(ProblemSubmitVO::objToVo).collect(Collectors.toList());
        voPage.setRecords(records);
        return getProblemSumbitVO(page,pageNum,pageSize);
    }


    @Override
    public SubmitSummaryVo getSubmitSummary(Long id) {
        SubmitSummaryVo summaryVo = new SubmitSummaryVo();
        User currentUser = userService.getById(id);

        //获取简单、中等、困难题目ids
        List<Long> easyIds = problemMapper.selectList(new LambdaQueryWrapper<Problem>()
                        .select(Problem::getId).eq(Problem::getDifficulty, ProblemDifficultyEnum.EASY.getValue()))
                .stream().map(Problem::getId).collect(Collectors.toList());
        List<Long> mediumIds = problemMapper.selectList(new LambdaQueryWrapper<Problem>()
                        .select(Problem::getId).eq(Problem::getDifficulty, ProblemDifficultyEnum.MEDIUM.getValue()))
                .stream().map(Problem::getId).collect(Collectors.toList());
        List<Long> hardIds = problemMapper.selectList(new LambdaQueryWrapper<Problem>()
                        .select(Problem::getId).eq(Problem::getDifficulty, ProblemDifficultyEnum.HARD.getValue()))
                .stream().map(Problem::getId).collect(Collectors.toList());
        int easyTotal = easyIds.size();
        int mediumTotal = mediumIds.size();
        int hardTotal = hardIds.size();
        summaryVo.setEasyTotal(easyTotal);
        summaryVo.setMediumTotal(mediumTotal);
        summaryVo.setHardTotal(hardTotal);
        summaryVo.setTotal(easyTotal + mediumTotal + hardTotal);

        //获取用户通过的简单、中等、困难题目数
        Integer easyPass = baseMapper.getPassCount(currentUser.getId(), easyIds);
        Integer mediumPass = baseMapper.getPassCount(currentUser.getId(), mediumIds);
        Integer hardPass = baseMapper.getPassCount(currentUser.getId(), hardIds);


        summaryVo.setEasyPass(easyPass);
        summaryVo.setMediumPass(mediumPass);
        summaryVo.setHardPass(hardPass);

        //获取用户提交总数
        Integer submitCount = Math.toIntExact(baseMapper.selectCount(new LambdaQueryWrapper<ProblemSubmit>()
                .eq(ProblemSubmit::getUserId, currentUser.getId())));
        summaryVo.setSubmitCount(submitCount);
        //获取用户成功的提交
        Integer passCount = Math.toIntExact(baseMapper.selectCount(new LambdaQueryWrapper<ProblemSubmit>()
                .eq(ProblemSubmit::getUserId, currentUser.getId())
                .eq(ProblemSubmit::getStatus, ProblemSubmitStatusEnum.SUCCEED.getValue())));


        summaryVo.setPassCount(passCount);
        return summaryVo;
    }
}




